// Copyright (C) 2009 Jesse Jones
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using MmmDoc;
using MmmDoc.Database;
using System;
using System.Collections.Generic;

namespace MmmDoc.Html
{
	/// <summary>Overloads for the <see cref = "WriteSignature"/> and <see cref = "WriteType"/> multimethods.</summary>
	internal static class WriteOverloads
	{
	#pragma warning disable 0169
		[WriteSignature.Overload]
		private static void WriteSignature(Writer writer, ConstructorMember member, bool sanitize)
		{
			if (!sanitize)
			{
				DoWriteModifiers(writer, member, sanitize);
			}
			
			DoWriteMemberName(writer, member, sanitize);
			DoWriteGenericArguments(writer, member.GenericArgs, sanitize);
			DoWriteArguments(writer, member.Arguments, sanitize);
		}
		
		[WriteSignature.Overload]
		private static void WriteSignature(Writer writer, EventMember member, bool sanitize)
		{
			if (!sanitize)
			{
				DoWriteModifiers(writer, member, sanitize);
				MmmDoc.Html.WriteType.Invoke(writer, member.Type, sanitize);
				writer.Write(" ");
				DoWriteKeyword(writer, "event ", sanitize);
			}
			
			DoWriteMemberName(writer, member, sanitize);
		}
		
		[WriteSignature.Overload]
		private static void WriteSignature(Writer writer, FieldMember member, bool sanitize)
		{
			if (!sanitize)
			{
				DoWriteModifiers(writer, member, sanitize);
				MmmDoc.Html.WriteType.Invoke(writer, member.Type, sanitize);
				writer.Write(" ");
			}
			
			DoWriteMemberName(writer, member, sanitize);
		}
		
		[WriteSignature.Overload]
		private static void WriteSignature(Writer writer, MethodMember member, bool sanitize)
		{
			if (!sanitize)
			{
				DoWriteModifiers(writer, member, sanitize);
				MmmDoc.Html.WriteType.Invoke(writer, member.ReturnType, sanitize);
				writer.Write(" ");
			}
			
			DoWriteMemberName(writer, member, sanitize);
			DoWriteGenericArguments(writer, member.GenericArgs, sanitize);
			DoWriteArguments(writer, member.Arguments, sanitize);
		}
		
		[WriteSignature.Overload]
		private static void WriteSignature(Writer writer, PropertyMember member, bool sanitize)
		{
			if (!sanitize)
			{
				DoWriteModifiers(writer, member, sanitize);
				MmmDoc.Html.WriteType.Invoke(writer, member.Type, sanitize);
				writer.Write(" ");
			}
			
			DoWriteMemberName(writer, member, sanitize);
			
			if (member.Arguments != null)
				DoWriteArguments(writer, member.Arguments, sanitize);
			
			if (!sanitize)
			{
				writer.Write(" {");
				
				if (member.GetterModifiers != null)
				{
					if (member.GetterModifiers.Length > 0)
						DoWriteKeyword(writer, member.GetterModifiers, sanitize);
					DoWriteKeyword(writer, "get;", sanitize);
				}
				
				if (member.SetterModifiers != null)
				{
					if (member.GetterModifiers != null)
						writer.Write(" ");
					
					if (member.SetterModifiers.Length > 0)
						DoWriteKeyword(writer, member.SetterModifiers, sanitize);
					DoWriteKeyword(writer, "set;", sanitize);
				}
				
				writer.Write("}");
			}
		}
		
		[WriteType.Overload]
		private static void WriteType(Writer writer, TypeGeneric type, bool sanitize)
		{
			MmmDoc.Html.WriteType.Invoke(writer, type.ElementType, sanitize);
			writer.Write("<");
			for (int i = 0; i < type.Arguments.Length; ++i)
			{
				MmmDoc.Html.WriteType.Invoke(writer, type.Arguments[i], sanitize);
				
				if (i + 1 < type.Arguments.Length)
					writer.Write(", ");
			}
			writer.Write(">");
		}
		
		[WriteType.Overload]
		private static void WriteType(Writer writer, TypeSpec type, bool sanitize)
		{
			MmmDoc.Html.WriteType.Invoke(writer, type.ElementType, sanitize);
			writer.Write(type.Suffix);
		}
		
		[WriteType.Overload]
		private static void WriteType(Writer writer, TypeDef type, bool sanitize)
		{
			if (sanitize)
				writer.Write(type.Name);
			else
				writer.WriteElement("A", type.FullName, "href", type.FileName() + ".html", "target", "_parent", "class", "type");
		}
		
		[WriteType.Overload]
		private static void WriteType(Writer writer, TypeName type, bool sanitize)
		{
			string alias;
			if (ms_aliases.TryGetValue(type.FullName, out alias))
			{
				if (sanitize)
					writer.Write(alias);
				else
					writer.WriteElement("SPAN", alias, "class", "type-alias");
			}
			else if (sanitize)
			{
				writer.Write(type.Name);
			}
			else
			{
				if (type is TypeDef)
					writer.WriteElement("SPAN", type.FullName, "class", "type-name");
				else
					writer.Write(type.Name);
			}
		}
		#pragma warning restore 0169
		
		#region Private Methods
		private static void DoWriteKeyword(Writer writer, string keyword, bool sanitize)
		{
			if (keyword.Length > 0)
			{
				if (sanitize)
					writer.Write(keyword);
				else
					writer.WriteElement("SPAN", keyword, "class", "keyword");
			}
		}
		
		private static void DoWriteModifiers(Writer writer, Member member, bool sanitize)
		{
			if (member.Modifiers.Length > 0)
			{
				if (sanitize)
					writer.Write(member.Modifiers);
				else
					writer.WriteElement("SPAN", member.Modifiers, "class", "keyword");
				writer.Write(" ");
			}
		}
		
		private static void DoWriteMemberName(Writer writer, Member member, bool sanitize)
		{
			if (sanitize)
				writer.Write(member.Name);
			else
				writer.WriteElement("SPAN", member.Name, "class", "member-name");
		}
		
		private static void DoWriteGenericArguments(Writer writer, TypeName[] args, bool sanitize)
		{
			if (args != null)
			{
				writer.Write("<");
				
				for (int i = 0; i < args.Length; ++i)
				{
					MmmDoc.Html.WriteType.Invoke(writer, args[i], sanitize);
					
					if (i + 1 < args.Length)
						writer.Write(", ");
				}
				
				writer.Write(">");
			}
		}
		
		private static void DoWriteArguments(Writer writer, Arguments args, bool sanitize)
		{
			writer.Write(args.Opener);
			
			for (int i = 0; i < args.Names.Length; ++i)
			{
				if (args.Modifiers[i] != null)
					DoWriteKeyword(writer, args.Modifiers[i], sanitize);
				
				MmmDoc.Html.WriteType.Invoke(writer, args.Types[i], sanitize);
				writer.Write(" ");
				
				if (sanitize)
					writer.Write(args.Names[i]);
				else
					writer.WriteElement("SPAN", args.Names[i], "class", "member-arg-name");
				
				if (i + 1 < args.Names.Length)
					writer.Write(", ");
			}
			
			writer.Write(args.Closer);
		}
		#endregion
		
		#region Fields		
		private static Dictionary<string, string> ms_aliases = new Dictionary<string, string>
		{
			{"System.Boolean", "bool"},
			{"System.Byte", "byte"},
			{"System.Char", "char"},
			{"System.Decimal", "decimal"},
			{"System.Double", "double"},
			{"System.Int16", "short"},
			{"System.Int32", "int"},
			{"System.Int64", "long"},
			{"System.SByte", "sbyte"},
			{"System.Object", "object"},
			{"System.Single", "float"},
			{"System.String", "string"},
			{"System.UInt16", "ushort"},
			{"System.UInt32", "uint"},
			{"System.UInt64", "ulong"},
			{"System.Void", "void"},
		};
		#endregion
	}
}
